#include "reference.hpp"
#include <memory>
#include <assert.h>
using namespace learn;
using namespace std;

int referenceTest::foo4(referenceTest *&&a)
{
    cout<<&a<<endl;
    cout<<a<<endl;
    a->h = 5;
    return 0;
}

int referenceTest::foo2(int &a)
{
    std::cout<<"foo2-->a:"<<&a<<std::endl;
    return 0;
}

int referenceTest::foo1(int &&a)
{
    std::cout<<"foo1-->a:"<<&a<<std::endl;
    foo2(a);
    return 0;
}

int referenceTest::foo(int &&a)
{
    int b = a;
    std::cout<<"foo->a:"<<&a<<std::endl;
    foo1(std::forward<int&&>(a));
    return b;
}

int learn::referencetest(void)
{
    referenceTest ref;
    int && a = 1; //右值引用，a是一个变量(左值)，指向立即数1的存储地址(右值)
    int & b = a; //b是左值引用，指向a,其实在编译后，a就是b，即a b变量存储的地址一致。
    int c = 2; //c是变量，保存了常量2
    int && d = std::move<int&>(c); //转移c的右值给d
    std::cout<<__func__<<"c : d"<<&c<<":"<<&d<<std::endl;
    //ref.foo(b); //错误，不能把a(变量，左值)作为右值参数
    //ref.foo(a);//错误，不能把a(变量，左值)作为右值参数
    ref.foo(std::move(a));//正确，a是右值的引用，但a本身是左值，通过move的方式，把a指向的立即数，转移给foo函数入参
    ref.foo(std::forward<int&&>(a)); //参数a转换成入参需求的类型，再传入，这里是转成右值引用

    auto f = [&, a](int&& c, int&& d)->int{ return a + b + c + d;};
    //f(move<int&>(c),a); //错误，a是左值，不能传入
    f(move<int&>(a),1); //正确，1和move<int&>(a)都是右值
    f(forward<int&&>(a),1); //正确，1和forward<int&&>(a)都是右值

    auto f1 = [&, a](int&& c, int& d)->int{ return a + b + c + d;};
    f1(move<int&>(c),a); //正确
    //f1(move<int&>(a),1); //错误，1是右值，不能传入
    f1(move<int&>(a),forward<int&>(a)); //正确，move<int&>(a)取出a的值，传入了int&&类型，forward<int&>(a)传入了int&

    auto p = std::make_unique<referenceTest>(); //申请智能指针对象
    cout<<p.get()<<endl;
    auto q = std::move(p); //p本身是左值，move(p)把p指向的内存，给到了q，即内存转移拥有者，q重置成nullptr。其实就是指针变量间的赋值
    cout<<p.get()<<endl; //此时p保存的是0
    cout<<q.get()<<endl; //此时q保存的是之前的p保存的值
    assert(p == nullptr);  // OK: reset to default
    p.reset(new referenceTest());
    assert(p != nullptr);       // OK: reset to int*(2
    cout<<p.get()<<endl; //此时p保存的是新的内存地址

    int r = 12;
    cout<<&r<<endl; //这里变量r的存储地址是0x7fffa4072688，而在foo3内部的r变量，存储地址是0x7fffa40726a0
                    //而r保存的值12，没有改变。说明int *&&可以把指针变量的右值引用，传递进去。
    ref.foo3(&r); //入参类型要求int *&&,即int指针的右值引用。这里即把12传入到foo3

    cout<<"----------------------"<<endl;
    cout<<&ref<<endl; //这里地址和foo4中ref地址一致，说明这个ref对象，是内存所在地址被传进去的。
    ref.foo4(&ref);
    cout<<ref.h<<endl;
    cout<<"----------------------"<<endl;

    auto sh_p = std::make_shared<referenceTest>(); //共享的智能指针
    sh_p->h = 55;
    cout<<sh_p->h<<endl;
    cout<<sh_p<<endl;
    cout<<&sh_p<<endl;
    cout<<"----------------------"<<endl;
    return 0;
}